import * as CANNON from "cannon-es";
// import * as CANNON from "cannon";
import * as THREE from "three";
import { toRaw } from "vue";
import * as BufferGeometryUtils from "three/addons/utils/BufferGeometryUtils.js";
import { ImprovedNoise } from "three/addons/math/ImprovedNoise.js";
import CannonDebugger from "cannon-es-debugger";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
//引入性能监视器stats.js
import Stats from "three/addons/libs/stats.module.js";
import { Player, playerState } from "./player";
import { loadGLTF } from "../loader";

//设置碰撞组，数值要用2的幂，子网掩码
enum Collision {
  GROUP1 = 1,
  GROUP2 = 2,
  GROUP3 = 4,
}
export class World {
  scene: any;
  camera: any;
  player: any;
  world: any;
  cannonDebugger: any;
  cannonMeshes: any[] = [];
  gravityY: any = -9.82;
  concreteMaterial: any; //混凝土
  plasticMaterial: any; //塑料
  boxMaterial: any;
  capsuleMaterial: any; //胶囊
  defaultMaterial: any;
  contactsTotals: number = 0; //开始时与当前世界中发生碰撞的所有接触点
  bodies: any = []; //刚体列表
  meshes: any = []; //几何列表
  guiObj: any;

  constructor(scene, camera) {
    this.scene = scene;
    this.camera = camera;
    this.init();
    // this.watchCollide();
    this.createCannonGui();
    // this.createStatsTool();
  }
  init() {
    this.world = new CANNON.World();
    //设置重力
    this.world.gravity.set(0, this.gravityY, 0);
    //碰撞算法,SAPBroadphase基于轴的广相位算法（基于轴投影，然后检查投影是否重叠
    this.world.broadphase = new CANNON.SAPBroadphase(this.world);
    //提升性能
    this.world.solver.iterations = 10; //解算器迭代次数，越多越精确，但性能下降
    this.world.allowSleep = true; //移动缓慢时，刚体睡眠不进行碰撞计算
    //创建材质
    this.createMaterial();
    //创建刚体
    this.loadModel("./model/collision-world.glb");
    // this.loadModel("./model/modular_dungeon.glb");
    // this.loadModel("./model/world.glb");

    this.createPlayerBody();
    // this.createPlayerMesh();
    // this.createShpereBody();
    // this.createShpereMesh();
    // this.createPlaneBody(new THREE.Vector3(0, 0, 0), 0.5);
    // this.createPlaneMesh(new THREE.Vector3(0, 0, 0), 1);
    // this.createPlaneBody(new THREE.Vector3(0, -1.3, 5), 0.05);
    // this.createPlaneMesh(new THREE.Vector3(0, -1.3, 5), 0.1);
    // this.createRoomBody(this.createRoomMesh());
    // this.createRoomBody(this.createRoomMesh2());
    // this.createRoomBody(this.createPlaneMesh(new THREE.Vector3(0, 0, 0), 0.1));

    console.log("世界所有刚体", this.world.bodies);
    this.world.bodies.forEach((body, index) => {
      console.log("刚体" + index + "位置：", body.position);
    });
  }
  createCannonGui() {
    //物理世界可视化
    let that = this;
    this.cannonDebugger = CannonDebugger(this.scene, this.world, {
      onInit(body: CANNON.Body, mesh: any) {
        mesh.visible = true;
        that.cannonMeshes.push(mesh);
      },
    });

    // 创建GUI
    const gui = new GUI();
    this.guiObj = {
      CannonDebugger: false,
    };
    gui
      .add(this.guiObj, "CannonDebugger")
      .name("CannonDebugger mesh visible")
      .onChange((value: boolean) => {
        if (value) {
          this.cannonMeshes.forEach((item) => {
            item.visible = true;
          });
        } else {
          this.cannonMeshes.forEach((item) => {
            item.visible = false;
          });
        }
      });
  }
  createStatsTool() {
    //FPS：最后一秒的帧数，越大越流畅
    //MS：渲染一帧需要的时间（毫秒），越低越好
    //MB：占用的内存信息
    var stats = new Stats();
    stats.showPanel(1);
    document.body.appendChild(stats.dom);
  }
  createMaterial() {
    this.concreteMaterial = new CANNON.Material("concrete");
    this.plasticMaterial = new CANNON.Material("plastic");
    this.boxMaterial = new CANNON.Material("boxMaterial");
    this.boxMaterial.friction = 0.1; //0,使其在斜面上滑行
    this.boxMaterial.restitution = 0; //归还
    this.capsuleMaterial = new CANNON.Material("capsuleMaterial");
    this.capsuleMaterial.friction = 0.3;
    // 定义两个刚体相遇后会发生什么
    const concretePlasticContactMaterial = new CANNON.ContactMaterial( //(材质1，材质2，碰撞属性对象)
      this.concreteMaterial,
      this.plasticMaterial,
      {
        friction: 0.1,
        restitution: 0.7,
      }
    );
    this.world.addContactMaterial(concretePlasticContactMaterial);

    this.defaultMaterial = new CANNON.Material("default");
    const defaultContactMaterial = new CANNON.ContactMaterial(
      this.defaultMaterial,
      this.defaultMaterial,
      {
        contactEquationStiffness: 1e7, //刚性
        frictionEquationRelaxation: 3, //松弛
        frictionEquationStiffness: 1e7, //刚性
        friction: 0.1, //摩擦.0.3
        restitution: 0.7, //归还,0.3
      }
    );
    this.world.addContactMaterial(defaultContactMaterial);
    this.world.defaultContactMaterial = defaultContactMaterial; //世界默认材质
  }
  update(delta) {
    var fixedTimeStep = 1.0 / 60.0; // 固定时间步长
    var maxSubSteps = 3; // 最大子步数
    this.world.step(fixedTimeStep, delta, maxSubSteps);
    this.cannonDebugger.update();
    // 将刚体的位置、旋转和缩放同步到Mesh对象上
    // this.meshes.forEach((item, index) => {
    //   if (!playerState.isMove) {
    //     this.meshes[index].position.copy(toRaw(this.bodies[index].position));
    //     this.meshes[index].quaternion.copy(
    //       toRaw(this.bodies[index].quaternion)
    //     );
    //   }
    // });

    // 脱离模型,还原到圆心,重力加速度要设置为0，避免向下砸的效果
    if (playerState.playerBody.position.y < -20) {
      playerState.playerBody.position.set(0, 0, 0);
      playerState.playerBody.quaternion.copy(new CANNON.Quaternion(0, 0, 0, 1));
      //归零设置
      playerState.playerBody.velocity.setZero();
      playerState.playerBody.initVelocity.setZero();
      playerState.playerBody.angularVelocity.setZero();
      playerState.playerBody.initAngularVelocity.setZero();
    }

    playerState.playerMesh?.position.copy(
      toRaw(playerState.playerBody.position)
    );

    this.player && this.player.update(delta);
  }
  createPlayerBody() {
    // 创建圆柱刚体
    // const shape = new CANNON.Cylinder(0.25,0.25,1,20);
    // playerState.playerBody = new CANNON.Body({
    //   mass: 1,
    //   position: new CANNON.Vec3(0, 3, 0),
    //   shape,
    //   material: this.plasticMaterial,
    //   collisionFilterGroup: Collision.GROUP2,
    //   collisionFilterMask: Collision.GROUP1 | Collision.GROUP3,
    // });
    // this.world.addBody(playerState.playerBody);

    // const shape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5)); // 设置立方体一半的尺寸
    const radius = 0.25;
    const height = 1;
    const shape = new CANNON.Sphere(radius);
    playerState.playerBody = new CANNON.Body({
      mass: 1, //质量
      position: new CANNON.Vec3(0, 3, 0),
      material: this.boxMaterial,
      collisionFilterGroup: Collision.GROUP2,
      collisionFilterMask: Collision.GROUP1 | Collision.GROUP3,
      // collisionResponse: false, //只检测碰撞，不发生物理效果
      allowSleep: false, //允许休眠
    });
    //三个球体叠加
    playerState.playerBody.addShape(shape, new CANNON.Vec3(0, 0, 0));
    playerState.playerBody.addShape(shape, new CANNON.Vec3(0, height / 2, 0));
    playerState.playerBody.addShape(shape, new CANNON.Vec3(0, -height / 2, 0));
    // playerState.playerBody.velocity.set(0, 0, 0); //设置初始速度
    //禁用旋转，避免在碰撞之后进行反转
    playerState.playerBody.fixedRotation = true;
    playerState.playerBody.updateMassProperties();

    this.world.addBody(toRaw(playerState.playerBody));
  }
  createPlayerMesh() {
    let geo = new THREE.BoxGeometry(1, 1, 1);
    const material = new THREE.MeshBasicMaterial({
      color: 0xffff00,
      side: THREE.DoubleSide,
    });
    playerState.playerMesh = new THREE.Mesh(geo, material);
    this.scene.add(toRaw(playerState.playerMesh));
  }
  createShpereBody() {
    const shape = new CANNON.Sphere(0.5);
    const body = new CANNON.Body({
      mass: 1,
      shape,
      position: new CANNON.Vec3(0, 5, -1),
      material: this.plasticMaterial,
      collisionFilterGroup: Collision.GROUP3,
      collisionFilterMask: Collision.GROUP1 | Collision.GROUP2,
    });
    this.world.addBody(body);
    this.bodies.push(body);
  }
  createShpereMesh() {
    let geo = new THREE.SphereGeometry(0.5);
    const material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
    let mesh = new THREE.Mesh(geo, material);
    this.scene.add(mesh);
    this.meshes.push(mesh);
  }
  createPlaneBody(position, height) {
    //创建地面刚体
    // const floorShape = new CANNON.Plane();//CANNON.Plane 是 Cannon.js 中用于表示无限平面的类
    const floorShape = new CANNON.Box(new CANNON.Vec3(5, height, 5)); //是THREE.BoxGeometry的1/2
    const floorBody = new CANNON.Body({
      mass: 0, //地面不受物理重力影响，保持静止
      position,
      shape: floorShape,
      material: this.boxMaterial,
      collisionFilterGroup: Collision.GROUP1, // 设置立方体属于碰撞组
      collisionFilterMask: Collision.GROUP2 | Collision.GROUP3, // 发生碰撞的碰撞组的对象
    });
    floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1);
    // floorBody.quaternion.setFromAxisAngle(
    //   new CANNON.Vec3(1, 0, 0), //旋转轴
    //   -Math.PI / 2 + 0.1 //旋转角度
    // ); //默认物理世界添加的平面面向相机
    // 设置立方体的碰撞标志和碰撞组
    this.world.addBody(floorBody);
  }
  createPlaneMesh(position, height, color?) {
    // const planeGeo = new THREE.PlaneGeometry(10, 10);
    let planeGeo = new THREE.BoxGeometry(10, height, 10);
    const material = new THREE.MeshBasicMaterial({
      color: color ? 0x00ff00 : 0xffffff,
      side: THREE.DoubleSide,
    });
    const planeMesh = new THREE.Mesh(planeGeo, material);
    planeMesh.position.copy(position);
    // planeMesh.rotateX(-Math.PI / 2 + 0.1);
    planeMesh.rotateX(0.1);
    this.scene.add(planeMesh);
    return planeMesh;
  }
  createRoomMesh() {
    var roomGeometry = new THREE.BoxGeometry(30, 10, 20);
    var roomMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    var roomMesh = new THREE.Mesh(roomGeometry, roomMaterial);
    roomMesh.name = "room";
    roomMesh.position.set(0, -2, 0);
    this.scene.add(roomMesh);
    return roomMesh;
  }
  createRoomBody(mesh) {
    const vertices = mesh.geometry.attributes.position.array; //顶点数组
    const indices = mesh.geometry.index.array; //顶点索引数组
    // console.log(mesh.geometry);

    // // 将顶点数组转换为CANNON.Vec3类型的数组
    // const cannonVertices: CANNON.Vec3[] = [];
    // for (let i = 0; i < vertices.length; i += 3) {
    //   const cannonVec = new CANNON.Vec3(
    //     vertices[i],
    //     vertices[i + 1],
    //     vertices[i + 2]
    //   );
    //   cannonVertices.push(cannonVec);
    // }
    // // 将索引转换为面数组
    // const faces: number[][] = [];
    // for (let i = 0; i < indices.length; i += 3) {
    //   faces.push(indices[i], indices[i + 1], indices[i + 2]);
    // }
    // const shape = new CANNON.ConvexPolyhedron({
    //   vertices: cannonVertices,
    //   faces,
    // });

    const vertices_ = vertices.map((item) => {
      return item * 0.5;
    });
    const shape = new CANNON.Trimesh(vertices_, indices); //只能与球体和平面进行碰撞
    const roomBody = new CANNON.Body({
      mass: 0,
      position: mesh.position,
      quaternion: mesh.quaternion,
      material: this.boxMaterial,
      shape: shape,
      collisionFilterGroup: Collision.GROUP1, //设置立方体属于碰撞组
      collisionFilterMask: Collision.GROUP2 | Collision.GROUP3, //设置立方体与碰撞组发生碰撞的对象
    });

    this.world.addBody(roomBody);

    // this.meshes.push(mesh);
    // this.bodies.push(roomBody);

    // let physics = new THREE.Box3().setFromObject(mesh);
    // var roomBody = new CANNON.Body({ mass: 0 });
    // roomBody.material = this.defaultMaterial;
    // roomBody.addShape(
    //   new CANNON.Box(physics.max.sub(physics.min).multiplyScalar(0.5))
    // );
    // roomBody.position.copy(mesh.position);
    // roomBody.quaternion.copy(mesh.quaternion);
    // this.world.addBody(roomBody);
  }
  loadModel(url) {
    // 加载模型，并渲染到画布上
    loadGLTF(url).then((object: any) => {
      this.scene.add(object.scene);
      // 遍历场景中的所有几何体数据
      object.scene.traverse((child: any) => {
        // console.log(child);
        // 遍历模型对象创建刚体对象
        this.traversechild(child);
        this.player = new Player(this.scene, this.camera);
      });
    });
  }
  createRoomMesh2() {
    const worldWidth = 128,
      worldDepth = 128;
    const worldHalfWidth = worldWidth / 2;
    const worldHalfDepth = worldDepth / 2;

    const generateHeight = (width, height) => {
      const data_: any = [],
        perlin = new ImprovedNoise(),
        size = width * height,
        z = Math.random() * 100;

      let quality = 2;

      for (let j = 0; j < 4; j++) {
        if (j === 0) for (let i = 0; i < size; i++) data_[i] = 0;

        for (let i = 0; i < size; i++) {
          const x = i % width,
            y = (i / width) | 0;
          data_[i] += perlin.noise(x / quality, y / quality, z) * quality;
        }

        quality *= 4;
      }

      return data_;
    };
    const data = generateHeight(worldWidth, worldDepth);

    const getY = (x, z) => {
      return (data[x + z * worldWidth] * 0.15) | 0;
    };

    // sides
    const matrix = new THREE.Matrix4(); //矩阵

    const pxGeometry: any = new THREE.PlaneGeometry(100, 100);
    pxGeometry.attributes.uv.array[1] = 0.5;
    pxGeometry.attributes.uv.array[3] = 0.5;
    pxGeometry.rotateY(Math.PI / 2);
    pxGeometry.translate(50, 0, 0);

    const nxGeometry: any = new THREE.PlaneGeometry(100, 100);
    nxGeometry.attributes.uv.array[1] = 0.5;
    nxGeometry.attributes.uv.array[3] = 0.5;
    nxGeometry.rotateY(-Math.PI / 2);
    nxGeometry.translate(-50, 0, 0);

    const pyGeometry: any = new THREE.PlaneGeometry(100, 100);
    pyGeometry.attributes.uv.array[5] = 0.5;
    pyGeometry.attributes.uv.array[7] = 0.5;
    pyGeometry.rotateX(-Math.PI / 2);
    pyGeometry.translate(0, 50, 0);

    const pzGeometry: any = new THREE.PlaneGeometry(100, 100);
    pzGeometry.attributes.uv.array[1] = 0.5;
    pzGeometry.attributes.uv.array[3] = 0.5;
    pzGeometry.translate(0, 0, 50);

    const nzGeometry: any = new THREE.PlaneGeometry(100, 100);
    nzGeometry.attributes.uv.array[1] = 0.5;
    nzGeometry.attributes.uv.array[3] = 0.5;
    nzGeometry.rotateY(Math.PI);
    nzGeometry.translate(0, 0, -50);

    //点云
    const geometries: any = [];

    for (let z = 0; z < worldDepth; z++) {
      for (let x = 0; x < worldWidth; x++) {
        const h = getY(x, z);

        matrix.makeTranslation(
          x * 100 - worldHalfWidth * 100,
          h * 100,
          z * 100 - worldHalfDepth * 100
        );
        const px = getY(x + 1, z);
        const nx = getY(x - 1, z);
        const pz = getY(x, z + 1);
        const nz = getY(x, z - 1);

        geometries.push(pyGeometry.clone().applyMatrix4(matrix));

        if ((px !== h && px !== h + 1) || x === 0) {
          geometries.push(pxGeometry.clone().applyMatrix4(matrix));
        }

        if ((nx !== h && nx !== h + 1) || x === worldWidth - 1) {
          geometries.push(nxGeometry.clone().applyMatrix4(matrix));
        }

        if ((pz !== h && pz !== h + 1) || z === worldDepth - 1) {
          geometries.push(pzGeometry.clone().applyMatrix4(matrix));
        }

        if ((nz !== h && nz !== h + 1) || z === 0) {
          geometries.push(nzGeometry.clone().applyMatrix4(matrix));
        }
      }
    }

    const geometry = BufferGeometryUtils.mergeGeometries(geometries);
    geometry.computeBoundingSphere();

    const texture = new THREE.TextureLoader().load(
      "https://img1.baidu.com/it/u=3500513686,1508017772&fm=253&fmt=auto&app=138&f=JPEG?w=670&h=447"
    );
    texture.colorSpace = THREE.SRGBColorSpace;
    texture.magFilter = THREE.NearestFilter;

    const mesh = new THREE.Mesh(
      geometry,
      new THREE.MeshLambertMaterial({ map: texture, side: THREE.DoubleSide })
    );
    this.scene.add(mesh);
    return mesh;
  }
  traversechild(child) {
    if (child instanceof THREE.Group) {
      child.children.forEach((element) => {
        this.traversechild(element);
      });
    } else if (child.isMesh) {
      child.castShadow = true;
      child.receiveShadow = true;
      this.createRoomBody(child);
    }
  }
  watchCollide() {
    // 设置碰撞检测回调函数，beginContact当两个物体接触时，preStep在模拟每个时间步之前触发
    this.world.addEventListener("beginContact", (e) => {
      console.log(e);
      // console.log(this.contactsTotals, e.target.contacts.length);
      if (this.contactsTotals < e.target.contacts.length) {
        this.contactsTotals = e.target.contacts.length;
      } else if (e.target.contacts.length < this.contactsTotals) {
        playerState.isCrash = true;
      } else {
        playerState.isCrash = false;
      }
    });

    playerState.playerBody.addEventListener("collide", (event) => {
      //监听碰撞.无法确定碰撞接触的是什么
      console.log(event.contact);
      // console.log(event.body === playerState.playerBody);
    });
  }
}
